package com.xuyuan.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.TimeZone;


public class JsonUtil {
    static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class);

    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final ObjectMapper objectMapper2 = new ObjectMapper();

    // 日起格式化
    private static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";

    private static final String Time_Zone = "GMT+8";

    static {
        // 对象的所有字段全部列入
        objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        // 取消默认转换timestamps形式
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        // 忽略空Bean转json的错误
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 所有的日期格式都统一为以下的样式，即yyyy-MM-dd HH:mm:ss
        objectMapper.setDateFormat(new SimpleDateFormat(STANDARD_FORMAT));

        objectMapper.setTimeZone(TimeZone.getTimeZone(Time_Zone));
        // 忽略 在json字符串中存在，但是在java对象中不存在对应属性的情况。防止错误
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 空字段不列入
        objectMapper2.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
        // 取消默认转换timestamps形式
        objectMapper2.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        // 按照字母顺序序列化字段
        objectMapper2.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
        objectMapper2.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
        // 忽略空Bean转json的错误
        objectMapper2.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 所有的日期格式都统一为以下的样式，即yyyy-MM-dd HH:mm:ss
        objectMapper2.setDateFormat(new SimpleDateFormat(STANDARD_FORMAT));
        // 忽略 在json字符串中存在，但是在java对象中不存在对应属性的情况。防止错误
        objectMapper2.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    /**
     * 对象转Json格式字符串
     *
     * @param obj 对象
     * @return Json格式字符串
     */
    public static <T> String obj2String(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            LOGGER.warn("Parse Object to String errorcode : {}", e.getMessage());
            throw new RuntimeException("Parse Object to String errorcode");
        }
    }

    /**
     * 对象转Json格式字符串
     *
     * @param obj 对象
     * @return Json格式字符串
     */
    public static <T> String obj2StringIgnoreNull(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : objectMapper2.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            LOGGER.warn("Parse Object to String errorcode : {}", e.getMessage());
            throw new RuntimeException("Parse Object to String errorcode");
        }
    }

    /**
     * 对象转Json格式字符串(格式化的Json字符串)
     *
     * @param obj 对象
     * @return 美化的Json格式字符串
     */
    public static <T> String obj2StringPretty(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            LOGGER.warn("Parse Object to String errorcode : {}", e.getMessage());
            throw new RuntimeException("Parse Object to String errorcode");
        }
    }

    /**
     * 美化json字符串，易读性更高
     *
     * @param jsonString
     * @return
     */
    public static String format(String jsonString) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            Object json = mapper.readValue(jsonString, Object.class);
            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json);
        } catch (JsonParseException | JsonMappingException e) {
            return jsonString;
        } catch (IOException e) {
            return jsonString;
        }
    }

    /**
     * 字符串转换为自定义对象
     *
     * @param str   要转换的字符串
     * @param clazz 自定义对象的class对象
     * @return 自定义对象
     */
    public static <T> T string2Obj(String str, Class<T> clazz) {
        if (StringUtils.isEmpty(str) || clazz == null) {
            return null;
        }
        try {
            return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
        } catch (Exception e) {
            LOGGER.warn("Parse String to Object errorcode : {}", e.getMessage());
            throw new RuntimeException("Parse String to Object errorcode");
        }
    }

    /**
     * 可以为list对象的转换，也可以是普通对象
     *
     * @param str
     * @param typeReference ，如果要转换成List<XX>，TypeReference<List<XX>> typeReference = new TypeReference()
     * @param <T>
     * @return
     */
    public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
        if (StringUtils.isEmpty(str) || typeReference == null) {
            return null;
        }
        try {
            return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
        } catch (IOException e) {
            LOGGER.warn("Parse String to Object errorcode", e);
            throw new RuntimeException("Parse String to Object errorcode");
        }
    }

    public static <T> T obj2Obj(Object obj, TypeReference<T> typeReference) {
        if (obj == null) {
            return null;
        }
        return objectMapper.convertValue(obj, typeReference);
    }

    public static <T> T obj2Obj(Object obj, Class<T> targetClass) {
        if (obj == null) {
            return null;
        }
        return objectMapper.convertValue(obj, targetClass);
    }

    /**
     * 转换成集合类型，
     *
     * @param str
     * @param collectionClazz 集合的类型  ArrayList.class
     * @param elementClasses  集合内泛型的类型 XX.class
     * @param <T>
     * @return ArrayList<XX>
     */
    public static <T> T string2Obj(String str, Class<?> collectionClazz, Class<?>... elementClasses) {
        JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClazz, elementClasses);
        try {
            return objectMapper.readValue(str, javaType);
        } catch (IOException e) {
            LOGGER.warn("Parse String to Object errorcode : {}" + e.getMessage());
            throw new RuntimeException("Parse String to Object errorcode");
        }
    }

    public static ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    /**
     * 全局替换json指定key的值
     *
     * @param jsonStr     json字符串
     * @param replaceKeys 替换的key
     * @return
     */
    public static String replace(String jsonStr, List<Pair<String, String>> replaceKeys) {
        if (StringUtils.isBlank(jsonStr)) {
            return jsonStr;
        }
        ObjectMapper mapper = getObjectMapper();
        try {
            JsonNode jsonNode = mapper.readTree(jsonStr);
            replaceJsonValue(jsonNode, replaceKeys);
            return mapper.writeValueAsString(jsonNode);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    private static void replaceJsonValue(JsonNode node, List<Pair<String, String>> replaceKeys) {
        if (node.isObject() && replaceKeys != null) {
            replaceKeys.forEach(pair -> {
                if (!node.has(pair.getLeft())) {
                    return;
                }
                ((ObjectNode) node).put(pair.getLeft(), pair.getRight());
            });
        }
        if (node.isArray()) {
            for (JsonNode element : node) {
                replaceJsonValue(element, replaceKeys);
            }
        } else if (node.isObject()) {
            for (JsonNode child : node) {
                replaceJsonValue(child, replaceKeys);
            }
        }
    }

    public static <T> JsonNode convertValueToJsonNode(T source) {
        return objectMapper2.convertValue(source, JsonNode.class);
    }

}
